#include "pch.h"
#include "avchat_component.h"
#include "avchat_component_def.h"
#include "base/win32/path_util.h"
#include "wrapper/avchat_business_wrapper.h"
#include "app_sdk_interface.h"
#include "module/video_manager_g2.h"

namespace nim_comp
{
	using Signaling = nim::Signaling;
	using namespace nim;
	using namespace std::placeholders;

	const int iCallingTimeoutSeconds = 45;
	bool parseCustomInfo(const std::string& str, bool& isFromGroup, std::string& requestId, std::string& channelName);
	int64_t getUid(const std::list<SignalingMemberInfo>& list, const std::string& accid);

	AvChatComponent::AvChatComponent()
	{
		isCameraOpen = true;
		timeOutHurryUp = false;
		isMasterInvited = false;
		isUseRtcSafeMode = false;
		avchat_type = kAvChatP2P;
	}
	AvChatComponent::~AvChatComponent()
	{
		if (rtcEngine_)
		{
			rtcEngine_->release();
			destroyNERtcEngine((void*&)rtcEngine_);
			rtcEngine_ = nullptr;
		}
	}

	void AvChatComponent::release()
	{
		if (rtcEngine_)
		{
			QLOG_APP(L"AvChatComponent release");
			rtcEngine_->release();
			destroyNERtcEngine((void*&)rtcEngine_);
			rtcEngine_ = nullptr;
		}
	}

	void AvChatComponent::setupAppKey(const std::string& key, bool useRtcSafeMode)
	{
		appKey_ = key;
		isUseRtcSafeMode = useRtcSafeMode;
		//创建并初始化engine；
		assert(!rtcEngine_);
		rtcEngine_ = (nertc::IRtcEngineEx*)createNERtcEngine();
		assert(rtcEngine_);

		std::string logPath  = nbase::UTF16ToUTF8(nbase::win32::GetLocalAppDataDir().append(AVCHAT_LOG_DIR));

		nertc::NERtcEngineContext context;
		context.app_key = appKey_.c_str();
		context.event_handler = this;
		context.log_dir_path = logPath.c_str();//AVCHAT_LOG_DIR;
		context.log_level = nertc::kNERtcLogLevelDebug;
		context.log_file_max_size_KBytes = 0;
		//context.log_dir_path = ""
		int ret = rtcEngine_->initialize(context);
		assert(ret == 0);
		ret = rtcEngine_->setAudioProfile(nertc::kNERtcAudioProfileStandardExtend, nertc::kNERtcAudioScenarioSpeech);
		if (0 != ret) {
			QLOG_APP(L"setAudioProfile failed!");
		}
		Signaling::RegOnlineNotifyCb(nbase::Bind(&AvChatComponent::signalingNotifyCb, this, std::placeholders::_1));
		Signaling::RegMutilClientSyncNotifyCb(nbase::Bind(&AvChatComponent::signalingMutilClientSyncCb, this, std::placeholders::_1));
		Signaling::RegOfflineNotifyCb(nbase::Bind(&AvChatComponent::signalingOfflineNotifyCb, this, std::placeholders::_1));
	}

	int AvChatComponent::setRecordDeviceVolume(int value)
	{
		nertc::IAudioDeviceManager* audio_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)&audio_device_manager);
		if (audio_device_manager)
		{
			int ret = audio_device_manager->adjustRecordingSignalVolume(value);
			if (ret == 0)
			{
				compEventHandler_.lock()->onAudioVolumeChanged(value, true);
			}
			return ret;
		}
		return 1;
	}
	int AvChatComponent::setPlayoutDeviceVolume(int value)
	{
		nertc::IAudioDeviceManager* audio_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)&audio_device_manager);
		if (audio_device_manager)
		{
			int ret = audio_device_manager->adjustPlaybackSignalVolume(value);
			if (ret == 0)
			{
				QLOG_APP(L"setPlayoutDeviceVolume seccess");
				compEventHandler_.lock()->onAudioVolumeChanged(value, false);
			}
			return ret;
		}
		return 1;
	}
	uint32_t AvChatComponent::getAudioVolumn(bool isRecord)
	{
		nertc::IAudioDeviceManager* audio_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)&audio_device_manager);
		uint32_t volume = 0;
		if (audio_device_manager)
		{
			isRecord ? audio_device_manager->getRecordDeviceVolume(&volume) 
				: audio_device_manager->getPlayoutDeviceVolume(&volume);
		}
		return volume;
	}
	void AvChatComponent::getLocalDeviceList(
		std::vector<std::wstring>* recordDevicesNames, 
		std::vector<std::wstring>* recordDevicesIds, 
		std::vector<std::wstring>* playoutDevicesNames, 
		std::vector<std::wstring>* playoutDevicesIds, 
		std::vector<std::wstring>* videoDeviceNames, 
		std::vector<std::wstring>* videoDeviceIds )
	{
		nertc::IAudioDeviceManager* audio_device_manager = nullptr;
		nertc::IVideoDeviceManager* video_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)&audio_device_manager);
		rtcEngine_->queryInterface(nertc::kNERtcIIDVideoDeviceManager, (void**)&video_device_manager);

		if (audio_device_manager)
		{
			nertc::IDeviceCollection* audio_record_collection = audio_device_manager->enumerateRecordDevices();
			for (int i=0; i<audio_record_collection->getCount(); i++)
			{
				char device_name[kNERtcMaxDeviceNameLength]{ 0 };
				char device_id[kNERtcMaxDeviceIDLength]{ 0 };
				int ret = audio_record_collection->getDevice(i, device_name, device_id);
				if (recordDevicesNames)
					recordDevicesNames->emplace_back(nbase::UTF8ToUTF16(device_name));
				if (recordDevicesIds)
					recordDevicesIds->emplace_back(nbase::UTF8ToUTF16(device_id));
			}

			nertc::IDeviceCollection* audio_playout_collection = audio_device_manager->enumeratePlayoutDevices();
			for (int i = 0; i <audio_playout_collection->getCount(); i++)
			{
				char device_name[kNERtcMaxDeviceNameLength]{ 0 };
				char device_id[kNERtcMaxDeviceIDLength]{ 0 };
				int ret = audio_playout_collection->getDevice(i, device_name, device_id);
				if (playoutDevicesNames)
					playoutDevicesNames->emplace_back(nbase::UTF8ToUTF16(device_name));
				if (playoutDevicesIds)
					playoutDevicesIds->emplace_back(nbase::UTF8ToUTF16(device_id));
			}
		}
		if (video_device_manager)
		{
			nertc::IDeviceCollection* video_collection = video_device_manager->enumerateCaptureDevices();
			for (int i = 0; i < video_collection->getCount(); i++)
			{
				char device_name[kNERtcMaxDeviceNameLength]{ 0 };
				char device_id[kNERtcMaxDeviceIDLength]{ 0 };
				int ret = video_collection->getDevice(i, device_name, device_id);
				if (videoDeviceNames)
					videoDeviceNames->emplace_back(nbase::UTF8ToUTF16(device_name));
				if (videoDeviceIds)
					videoDeviceIds->emplace_back(nbase::UTF8ToUTF16(device_id));
			}
		}
	}
	void AvChatComponent::setVideoDevice(const std::wstring& id)
	{
		nertc::IVideoDeviceManager* video_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDVideoDeviceManager, (void**)&video_device_manager);
		if (!video_device_manager)
			return;
		video_device_manager->setDevice(nbase::UTF16ToUTF8(id).c_str());
	}

	std::wstring AvChatComponent::getAudioDevice(bool isRecord)
	{
		nertc::IAudioDeviceManager* audio_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)&audio_device_manager);
		if (!audio_device_manager)
			return L"";

		char device_id[kNERtcMaxDeviceIDLength] = { 0 };
		isRecord ? audio_device_manager ->getRecordDevice(device_id)
			: audio_device_manager->getPlayoutDevice(device_id);

		return nbase::UTF8ToUTF16(device_id);
	}
	std::wstring AvChatComponent::getVideoDevice()
	{
		nertc::IVideoDeviceManager* video_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDVideoDeviceManager, (void**)&video_device_manager);
		if (!video_device_manager)
			return L"";

		char device_id[kNERtcMaxDeviceIDLength] = { 0 };
		video_device_manager->getDevice(device_id);

		return nbase::UTF8ToUTF16(device_id);
	}

	void AvChatComponent::setAudioDevice(const std::wstring& id, bool isRecord)
	{
		nertc::IAudioDeviceManager* audio_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)&audio_device_manager);

		if (!audio_device_manager)
			return;

		isRecord ? audio_device_manager->setRecordDevice(nbase::UTF16ToUTF8(id).c_str())
			: audio_device_manager->setPlayoutDevice(nbase::UTF16ToUTF8(id).c_str());
	}

	/**
	* P2P视频呼叫
	*/
	void AvChatComponent::call(const std::string& userId, AVCHAT_CALL_TYPE type, AvChatComponentOptCb cb)
	{
		channelMembers_.clear();
		requestId_.clear();
		channelName_.clear();
		nim::SignalingCreateParam createParam;
		createParam.channel_type_ = (nim::NIMSignalingType)type;
		toAccid.clear();
		toAccid.push_back(userId);
		callType = type;
		avchat_type = kAvChatP2P;
		optCb_ = cb;
		senderAccid = nim::Client::GetCurrentUserAccount();
		invitedInfo_ = SignalingNotifyInfoInvite();
		createdChannelInfo_ = SignalingCreateResParam();
		isMasterInvited = true;

		//1.创建channel
		auto createCb = nbase::Bind(&AvChatComponent::signalingCreateCb, this,  _1, _2, cb);
		Signaling::SignalingCreate(createParam, createCb);
		status_ = calling;
		startDialWaitingTimer();
		rtcEngine_->stopVideoPreview();
		stopAudioDeviceLoopbackTest();
	}

	/**
	* 群组视频呼叫
	*/
	void AvChatComponent::call(const std::vector<std::string>& members, AVCHAT_CALL_TYPE type, AvChatComponentOptCb cb)
	{
		channelMembers_.clear();
		requestId_.clear();
		channelName_.clear();
		nim::SignalingCreateParam createParam;
		createParam.channel_type_ = (nim::NIMSignalingType)type;
		toAccid = members;
		callType = type;
		avchat_type = kAvChatMulti;
		optCb_ = cb;
		senderAccid = nim::Client::GetCurrentUserAccount();
		invitedInfo_ = SignalingNotifyInfoInvite();
		createdChannelInfo_ = SignalingCreateResParam();
		isMasterInvited = true;

		//1.创建channel
		auto createCb = nbase::Bind(&AvChatComponent::signalingCreateCb, this, _1, _2, cb);
		Signaling::SignalingCreate(createParam, createCb);
		status_ = calling;
		startDialWaitingTimer();
		rtcEngine_->stopVideoPreview();
		stopAudioDeviceLoopbackTest();
	}

	void AvChatComponent::onWaitingTimeout()
	{
		if (status_ == calling)
		{
			closeChannelInternal(createdChannelInfo_.channel_info_.channel_id_, nullptr);
			compEventHandler_.lock()->onCallingTimeOut();
		}
		handleNetCallMsg(nim_comp::kNIMNetCallStatusTimeout);
	}
	void AvChatComponent::startDialWaitingTimer()
	{
		calling_timeout_timer_.Cancel();
		StdClosure timer_cb = nbase::Bind([this]() {
			if (status_ == calling)
			{
				//closeChannelInternal(createdChannelInfo_.channel_info_.channel_id_, nullptr);
				timeOutHurryUp = true;
				compEventHandler_.lock()->onCallingTimeOut();
				handleNetCallMsg(nim_comp::kNIMNetCallStatusTimeout);
			}
			});
		timer_cb = calling_timeout_timer_.ToWeakCallback(timer_cb);
		nbase::ThreadManager::PostDelayedTask(timer_cb, nbase::TimeDelta::FromSeconds(iCallingTimeoutSeconds));
	}

	// 被叫方发送ACCEPT
	void AvChatComponent::accept(AvChatComponentOptCb cb)
	{
		//信令accept（自动join）
		SignalingAcceptParam param;
		param.account_id_ = invitedInfo_.from_account_id_;
		param.auto_join_ = true;
		param.channel_id_ = invitedInfo_.channel_info_.channel_id_;
		param.request_id_ = invitedInfo_.request_id_;
		param.offline_enabled_ = true;
		param.uid_ = app_sdk::AppSDKInterface::GetInstance()->user_id;

		Json::Value values;
		values[kAvChatCallSingle] = (int)avchat_type;
		values[kAvChatCallRequestId] = requestId_;
		values[kAvChatCallChannelName] = channelName_;
		Json::FastWriter fw;
		param.accept_custom_info_ = fw.write(values);

		auto acceptCb = nbase::Bind(&AvChatComponent::signalingAcceptCb, this, _1, _2, cb);
		Signaling::Accept(param, acceptCb);
	}

	void AvChatComponent::reject(AvChatComponentOptCb cb)
	{
		//信令reject
		SignalingRejectParam param;
		param.account_id_ = invitedInfo_.from_account_id_;
		param.channel_id_ = invitedInfo_.channel_info_.channel_id_;
		param.request_id_ = invitedInfo_.request_id_;
		param.offline_enabled_ = true;

		auto rejectCb = nbase::Bind(&AvChatComponent::signalingRejectCb, this, _1, _2, cb);
		Signaling::Reject(param, rejectCb);

		invitedInfo_ = SignalingNotifyInfoInvite();
		handleNetCallMsg(nim_comp::kNIMNetCallStatusRejected);
	}

	void AvChatComponent::hangup(AvChatComponentOptCb cb)
	{
		rtcEngine_->leaveChannel();
		if (status_ == idle) 
		{
			QLOG_APP(L"The AvChatComponent status is idle, discard hangup operation");
			return;
		}
		if (status_ == calling && isMasterInvited)
		{
			for (size_t i = 0; i < invitingInfo_.size(); i++) {
				SignalingCancelInviteParam param;
				param.channel_id_ = invitingInfo_[i].channel_id_;
				param.account_id_ = invitingInfo_[i].account_id_;
				param.request_id_ = invitingInfo_[i].request_id_;
				param.offline_enabled_ = invitingInfo_[i].offline_enabled_;
				Signaling::CancelInvite(param, nullptr);
			}
		}

		if (timeOutHurryUp) { //来自超时的自动挂断,不需要发送NetCallMsg
			timeOutHurryUp = false;
		}
		else {
			handleNetCallMsg(nim_comp::kNIMNetCallStatusCanceled);
		}

		if (isMasterInvited)
		{
			//主动方调用挂断
			if (!invitingInfo_.empty()) closeChannelInternal(invitingInfo_[0].channel_id_, cb);
		}
		else
		{
			//被动方调用挂断
			closeChannelInternal(invitedInfo_.channel_info_.channel_id_, cb);
		}
	}

	//发起方取消呼叫
	void AvChatComponent::cancel(AvChatComponentOptCb cb)
	{
		if (isMasterInvited && status_ == calling)
		{
			for (size_t i = 0; i < invitingInfo_.size(); i++){
				SignalingCancelInviteParam param;
				param.channel_id_ = invitingInfo_[i].channel_id_;
				param.account_id_ = invitingInfo_[i].account_id_;
				param.request_id_ = invitingInfo_[i].request_id_;
				param.offline_enabled_ = invitingInfo_[i].offline_enabled_;
				Signaling::CancelInvite(param, nullptr);
			}
			rtcEngine_->leaveChannel();
			closeChannelInternal(createdChannelInfo_.channel_info_.channel_id_, cb);
		}
		else
		{
			QLOG_ERR(L"cancel error");
			if (cb) cb(0, 0);
		}
	}

	void AvChatComponent::leave(AvChatComponentOptCb cb)
	{
		rtcEngine_->leaveChannel();
		if (status_ == calling && isMasterInvited)
		{
			for (size_t i = 0; i < invitingInfo_.size(); i++) {
				SignalingCancelInviteParam param;
				param.channel_id_ = invitingInfo_[i].channel_id_;
				param.account_id_ = invitingInfo_[i].account_id_;
				param.request_id_ = invitingInfo_[i].request_id_;
				param.offline_enabled_ = invitingInfo_[i].offline_enabled_;
				Signaling::CancelInvite(param, nullptr);
			}
		}
		if (!joined_channel_id_.empty())
		{
			nim::SignalingLeaveParam param;
			param.channel_id_ = joined_channel_id_;
			param.offline_enabled_ = true;
			Signaling::Leave(param, nbase::Bind(&AvChatComponent::signalingLeaveCb, this, _1, _2, cb));
		}
		else
		{
			status_ = idle;
			QLOG_ERR(L"leave error: no joined channel exist.");
			if (cb) cb(0, 0);
		}
	}

	void AvChatComponent::setupLocalView(nertc::NERtcVideoCanvas* canvas)
	{
		assert(rtcEngine_ && canvas);
		int ret = rtcEngine_->setupLocalVideoCanvas(canvas);
		QLOG_APP(L"setupLocalVideoCanvas ret: {0}") << ret;
	}

	void AvChatComponent::setupRemoteView(nertc::NERtcVideoCanvas* canvas, nertc::uid_t uid)
	{
		assert(rtcEngine_);
		int ret = rtcEngine_->setupRemoteVideoCanvas(uid, canvas);
		QLOG_APP(L"setupRemoteVideoCanvas ret: {0}") << ret;
		ret = rtcEngine_->subscribeRemoteVideoStream(uid, nertc::kNERtcRemoteVideoStreamTypeHigh, true);
		QLOG_APP(L"subscribeRemoteVideoStream ret: {0}") << ret;
	}

	void AvChatComponent::switchCamera()
	{
		//PC暂不实现摄像头切换
		assert(false);
	}

	void AvChatComponent::enableLocalVideo(bool enable)
	{
		assert(rtcEngine_);
		int ret = rtcEngine_->enableLocalVideo(enable);
		QLOG_APP(L"enableLocalVideo ret: {0}") << ret;
	}
	//音频输入设备静音
	void AvChatComponent::muteLocalAudio(bool mute)
	{
		assert(rtcEngine_); 
		if (mute)
		{
			QLOG_APP(L"muteLocalAudio ");
		}
		int ret = rtcEngine_->enableLocalAudio(!mute);
		QLOG_APP(L"enableLocalAudio ret: {0}") << ret;
	}
	void AvChatComponent::enableAudioPlayout(bool enable)
	{
		rtcEngine_->muteLocalAudioStream(!enable);
	}

	void AvChatComponent::regEventHandler(std::shared_ptr<IAvChatComponentEventHandler> compEventHandler)
	{
		compEventHandler_.reset();
		compEventHandler_ = compEventHandler;
	}

	void AvChatComponent::startVideoPreview(bool start/* = true*/)
	{
		if (start)
			rtcEngine_->enableLocalVideo(true);
		start ? rtcEngine_->startVideoPreview() : rtcEngine_->stopVideoPreview();
	}

	void AvChatComponent::switchCallType(std::string user_id, int call_type)
	{
		if (rtcEngine_ != nullptr)
		{
			int64_t uid = channelMembers_[user_id];
			int ret = rtcEngine_->subscribeRemoteVideoStream(uid, nertc::kNERtcRemoteVideoStreamTypeHigh, false);
			//ret = rtcEngine_->subscribeRemoteAudioStream(uid, true);

			QLOG_APP(L"subscribeRemoteVideoStream ret: {0}") << ret;

			ret = rtcEngine_->muteLocalVideoStream(true);
			//ret = rtcEngine_->subscribeRemoteAudioStream(uid, true);

			QLOG_APP(L"enableVideoToAudio ret: {0}") << ret;

			Json::Value values;
			values["cid"] = 2; //cid = 2表示控制信令，表示触发被叫方视频转音频
			values["type"] = kAvChatAudio; ///***音频频道* /AUDIO(1), 视频频道VIDEO(2) */
			nim::SignalingControlParam controlParam;
			controlParam.channel_id_ = joined_channel_id_.empty()? AvChatBusinessWrapper::getChannelId(): joined_channel_id_;
			controlParam.account_id_ = user_id;
			controlParam.custom_info_ = values.toStyledString();

			auto controlCb = nbase::Bind(&AvChatComponent::signalingControlCb,
				this,
				std::placeholders::_1,
				std::placeholders::_2);
			//控制信令
			Signaling::Control(controlParam, controlCb);

		}
	}

	void AvChatComponent::startAudioDeviceLoopbackTest(int interval)
	{
		nertc::IAudioDeviceManager* audio_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)&audio_device_manager);

		if (audio_device_manager)
		{
			audio_device_manager->startAudioDeviceLoopbackTest(interval);
		}
	}

	void AvChatComponent::stopAudioDeviceLoopbackTest()
	{
		nertc::IAudioDeviceManager* audio_device_manager = nullptr;
		rtcEngine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)&audio_device_manager);
		if (audio_device_manager)
		{
			audio_device_manager->stopAudioDeviceLoopbackTest();
		}
	}

	void AvChatComponent::requestTokenValue(int64_t uid)
	{
		stoken_ = "xyz";
		if (isUseRtcSafeMode) {
			getTokenService_(uid, [=](const std::string token) {
				stoken_ = token;
				});
		}
		else stoken_ = "";
	}

	void AvChatComponent::setVideoQuality(nertc::NERtcVideoProfileType type)
	{
		nertc::NERtcVideoConfig config;
		config.max_profile = type;
		config.framerate = nertc::kNERtcVideoFramerateFps_15;
		config.crop_mode_ = nertc::kNERtcVideoCropModeDefault;
		rtcEngine_->setVideoConfig(config);
	}

	void AvChatComponent::setAudioMute(std::string user_id, bool bMute) {
		if (rtcEngine_) {
			int64_t uid = channelMembers_[user_id];
			rtcEngine_->subscribeRemoteAudioStream(uid, bMute);
		}
	}

	void AvChatComponent::closeChannelInternal(const std::string& channelId, AvChatComponentOptCb cb)
	{
		QLOG_APP(L"closeChannelInternal， channelId: {0}")<< channelId;
		SignalingCloseParam param;
		param.channel_id_ = channelId;
		param.offline_enabled_ = true;
		auto closeCb = nbase::Bind(&AvChatComponent::signalingCloseCb, this, _1, _2, cb);
		Signaling::SignalingClose(param, closeCb);
		isMasterInvited = false;
	}
	void AvChatComponent::signalingInviteCb(int errCode, std::shared_ptr<SignalingResParam> res_param, AvChatComponentOptCb cb)
	{
		//4.invite完毕后，call过程结束，调用cb返回结果
		QLOG_APP(L"signalingInviteCb error: errCode:{0}") << errCode;
		if (errCode != 200)
		{
			//closeChannelInternal(createdChannelInfo_.channel_info_.channel_id_, cb);
		}
		if (avchat_type == kAvChatP2P) status_ = calling;
		if (cb) cb(errCode, 0);
	}
	void AvChatComponent::signalingAcceptCb(int errCode, std::shared_ptr<nim::SignalingResParam> res_param, AvChatComponentOptCb cb)
	{
		SignalingAcceptResParam* res = (SignalingAcceptResParam*)res_param.get();
		QLOG_APP(L"signalingAcceptCb, errCOde: {0}") << errCode;

		if (errCode == 200)
		{
			updateChannelMembers(res);
			auto uid = getUid(res->info_.members_, invitedInfo_.to_account_id_);

			to_account_id_ = uid;
			joined_channel_id_ = res->info_.channel_info_.channel_id_;
			status_ = inCall;

			requestTokenValue(uid);
			while ("xyz" == stoken_)
			{
				std::this_thread::yield();
			}
			int type = res->info_.channel_info_.channel_type_;
			if (type == kAvChatAudio) {
				enableLocalVideo(false);
			}
			else if (type == kAvChatVideo) {
				enableLocalVideo(true);
			}
			int ret = rtcEngine_->joinChannel(stoken_.c_str(), channelName_.c_str(), uid);
			if (ret != 0) QLOG_ERR(L"nertc join channel failed: {0}") << ret;
			//if (cb) {
			//	uid = getUid(res->info_.members_, invitedInfo_.from_account_id_);
			//	cb(errCode, uid);
			//}
			//return;
		}
		if (cb) cb(errCode, 0);
	}

	void AvChatComponent::signalingRejectCb(int errCode, std::shared_ptr<nim::SignalingResParam> res_param, AvChatComponentOptCb cb)
	{
		QLOG_APP(L"signalingAcceptCb, errCOde: {0}") << errCode;
		if (cb) cb(errCode, 0);
		status_ = idle;
	}
	void AvChatComponent::signalingCloseCb(int errCode, std::shared_ptr<nim::SignalingResParam> res_param, AvChatComponentOptCb cb)
	{
		QLOG_APP(L"signalingCloseCb: errCode: {0}") << errCode;
		if (errCode == 200)
		{
			createdChannelInfo_ = SignalingCreateResParam();
			joined_channel_id_.clear();
		}
		else
		{
			QLOG_ERR(L"signalingCloseCb: errCode: {0}") << errCode;
		}
		status_ = idle;
		if (cb) cb(errCode, 0);
	}
	void AvChatComponent::signalingLeaveCb(int errCode, std::shared_ptr<nim::SignalingResParam> res_param, AvChatComponentOptCb cb)
	{
		QLOG_APP(L"signalingLeaveCb errCode: {0}") << errCode;
		if (errCode == 200)
		{
			joined_channel_id_.clear();
			status_ = idle;
		}
		if (cb) cb(errCode, 0);
	}
	void AvChatComponent::updateChannelMembers(const SignalingJoinResParam* res)
	{
		for (auto memInfo : res->info_.members_)
		{
			channelMembers_[memInfo.account_id_] = memInfo.uid_;
		}
	}
	std::string AvChatComponent::getAccid(int64_t uid)
	{
		for (auto it : channelMembers_)
		{
			if (it.second == uid)
				return it.first;
		}
		QLOG_ERR(L"Get accid failed, uid: {0}") << uid;
		return "";
	}
	void AvChatComponent::signalingJoinCb(int errCode, std::shared_ptr<SignalingResParam> res_param, AvChatComponentOptCb cb, const std::string& channelId)
	{
		if (errCode != 200)
		{
			QLOG_ERR(L"SignalingOptCallback error: errCode:{0}") << errCode;
			closeChannelInternal(createdChannelInfo_.channel_info_.channel_id_, nullptr);
			if (cb) cb(errCode, 0);
			return;
		}
		updateChannelMembers((SignalingJoinResParam*)res_param.get());

		int64_t uid;
		uid = channelMembers_[senderAccid];
		requestTokenValue(uid);
		Json::Value values;
		channelName_ = std::string(channelId).append("|").append(std::to_string(uid));
		values[kAvChatCallSingle] = (int)avchat_type;
		values[kAvChatCallRequestId] = channelId;
		values[kAvChatCallChannelName] = channelName_;
		Json::FastWriter fw;

		invitingInfo_.resize(toAccid.size());
		for (size_t i = 0; i < toAccid.size(); i++) {
			invitingInfo_[i].account_id_ = toAccid[i];
			invitingInfo_[i].channel_id_ = channelId;
			invitingInfo_[i].request_id_ = channelId;
			invitingInfo_[i].custom_info_ = fw.write(values);
			invitingInfo_[i].push_info_.need_push_ = true;
			invitingInfo_[i].push_info_.push_title_ = "视频邀请";
			invitingInfo_[i].push_info_.push_content_ = app_sdk::AppSDKInterface::GetInstance()->user_nickname + "邀请您视频通话";
			auto inviteCb = nbase::Bind(&AvChatComponent::signalingInviteCb, this, _1, _2, cb);
			QLOG_APP(L"Signaling::Invite, channelName: {0} toAccid: {1}") << channelName_ << toAccid[i];
			//3.信令 invite
			Signaling::Invite(invitingInfo_[i], inviteCb);
		}
		// 群组通话，发送邀请后直接先加入房间
		if (avchat_type == kAvChatMulti) {
			status_ = inCall;
			int ret = rtcEngine_->joinChannel(stoken_.c_str(), channelName_.c_str(), channelMembers_[senderAccid]);
			if (ret != 0) QLOG_ERR(L"nertc join channel failed: {0}") << ret;
		} else status_ = calling;
	}

	void AvChatComponent::signalingCreateCb(int errCode, std::shared_ptr<SignalingResParam> res_param, AvChatComponentOptCb cb)
	{
		if (errCode != 200)
		{
			QLOG_ERR(L"SignalingOptCallback error: errCode:{0}") << errCode;
			if (cb) cb(errCode, 0);
			return;
		}
		assert(res_param);

		SignalingCreateResParam* res = (SignalingCreateResParam*)res_param.get();
		createdChannelInfo_ = *res;

		nim::SignalingJoinParam joinParam;
		joinParam.channel_id_ = res->channel_info_.channel_id_;
		joinParam.uid_ = app_sdk::AppSDKInterface::GetInstance()->user_id;
		joinParam.offline_enabled_ = true;
		auto joinCb = nbase::Bind(&AvChatComponent::signalingJoinCb,
							this,
							std::placeholders::_1, 
							std::placeholders::_2,
							cb,
							res->channel_info_.channel_id_);
		//2.信令Join
		Signaling::Join(joinParam, joinCb);
	}

	void AvChatComponent::signalingControlCb(int errCode, std::shared_ptr<nim::SignalingResParam> res_param)
	{
		if (errCode != 200)
		{
			QLOG_ERR(L"SignalingOptCallback error: errCode:{0}") << errCode;
			if (optCb_) optCb_(errCode, 0);
			return;
		}
	}

	void AvChatComponent::handleControl(std::shared_ptr<nim::SignalingNotifyInfo> notifyInfo)
	{
		Json::Value values;
		Json::Reader reader;
		std::string info = notifyInfo->custom_info_;
		int type = notifyInfo->channel_info_.channel_type_;
		if (!reader.parse(info, values) || !values.isObject())
		{
			QLOG_ERR(L"parse custom info failed: {0}");
			return;
		}
		if (values["cid"].isInt()) {
			int value = values["cid"].asInt();
			//被叫方切换成音频模式
			if (value == 2) {
				compEventHandler_.lock()->OnVideoToAudio();
			}
		}
	}

	// 被叫收到INVITE，先获取token。如果是老版本发起呼叫，并等待控制信令，否则直接加入channelName，而不是原来的channelId。
	void AvChatComponent::handleInvited(std::shared_ptr<SignalingNotifyInfo> notifyInfo)
	{
		//抛出onInvite事件，更新UI、响应用户操作
		if (compEventHandler_.expired())
			return;

		bool isFromGroup = false;
		requestId_.clear();
		channelName_.clear();
		if (!parseCustomInfo(notifyInfo->custom_info_, isFromGroup, requestId_, channelName_))
		{
			assert(false);
			return;
		}
		QLOG_APP(L"handleInvited, from_account_id: {0}, requestId: {1}, channelName: {2}") << notifyInfo->from_account_id_ << requestId_ << channelName_;
		SignalingNotifyInfoInvite* inviteInfo = (SignalingNotifyInfoInvite*)notifyInfo.get();

		//忙线处理
		if (status_ != idle)
		{
			//信令reject
			SignalingRejectParam param;
			param.account_id_ = inviteInfo->from_account_id_;
			param.channel_id_ = inviteInfo->channel_info_.channel_id_;
			param.request_id_ = inviteInfo->request_id_;
			param.custom_info_ = "601";
			param.offline_enabled_ = true;

			Signaling::Reject(param, [](int errCode, std::shared_ptr<nim::SignalingResParam> res_param) {
				QLOG_APP(L"handle busy, Signaling::Reject return: {0}") << errCode;
				});
			//忙线方(被叫方)发送话单
			SendNetCallMsg(inviteInfo->from_account_id_,
				param.channel_id_,
				inviteInfo->channel_info_.channel_type_,
				(int)nim_comp::kNIMNetCallStatusBusy,
				std::vector<std::string>{inviteInfo->from_account_id_, nim::Client::GetCurrentUserAccount()},
				std::vector<int>{0, 0}
			);
			return;
		}

		//将收到邀请后的频道信息拷贝到内部，供accept使用
		invitedInfo_ = *inviteInfo;
		status_ = called;
		avchat_type = isFromGroup ? kAvChatMulti : kAvChatP2P;
		int type = inviteInfo->channel_info_.channel_type_;
		compEventHandler_.lock()->onInvited(notifyInfo->from_account_id_,
			std::vector<std::string>(),
			isFromGroup,
			AVCHAT_CALL_TYPE(type)
		);
	}

	void AvChatComponent::handleOtherClientAccepted(std::shared_ptr<SignalingNotifyInfo> notifyInfo)
	{
		//被叫方的通话邀请在其他端被接听，通知上层关闭界面、不关channel
		compEventHandler_.lock()->onOtherClientAccept();
		status_ = idle;
	}

	// 发起方收到被叫方的accept
	void AvChatComponent::handleAccepted(std::shared_ptr<SignalingNotifyInfo> notifyInfo)
	{
		SignalingNotifyInfoAccept* acceptedInfo = (SignalingNotifyInfoAccept*)notifyInfo.get();
		//SignalingNotifyInfoAccept tempacceptedInfo = *acceptedInfo;
		QLOG_APP(L"handleAccepted, from_account_id_: {0}, senderAccid: {1}") << acceptedInfo->from_account_id_ << senderAccid;
		if (acceptedInfo->to_account_id_ != senderAccid)
			return;
	
		bool isFromGroup = false;
		requestId_.clear();
		if (!acceptedInfo->custom_info_.empty())
		{
			std::string channelNameTmp;
			if (!parseCustomInfo(acceptedInfo->custom_info_, isFromGroup, requestId_, channelNameTmp))
			{
				assert(false);
				return;
			}
		}
		QLOG_APP(L"handleAccepted, requestId_: {0}") << requestId_;

		requestTokenValue(channelMembers_[senderAccid]);
		while ("xyz" == stoken_)
		{
			std::this_thread::yield();
		}

		if (callType == kAvChatAudio) enableLocalVideo(false);
		else if (callType == kAvChatVideo) enableLocalVideo(true);

		//auto selfUid = nim::Client::GetCurrentUserAccount();
		QLOG_APP(L"handleAccepted: strToken: {0}") << stoken_;
		if (avchat_type == kAvChatP2P)
		{
			status_ = inCall;
			int ret = rtcEngine_->joinChannel(stoken_.c_str(), channelName_.c_str(), channelMembers_[senderAccid]);
			if (ret != 0) QLOG_ERR(L"nertc join channel failed: {0}") << ret;
		}
		compEventHandler_.lock()->onUserAccept(acceptedInfo->from_account_id_);
		from_account_id_ = acceptedInfo->from_account_id_;
	}
	
	//处理异常情况下的话单
	void AvChatComponent::handleNetCallMsg(nim_comp::NIMNetCallStatus why)
	{
		if (status_ == inCall) return; //对于已经建立连接通话之后的挂断，不需要发送话单，由服务器发送
		if (isMasterInvited) {
			std::string channel_id = AvChatBusinessWrapper::getChannelId();
			std::string session_id = toAccid[0];
			bool is_video_mode_ = callType == AVCHAT_CALL_TYPE::kAvChatVideo ? true : false;
			SendNetCallMsg(session_id,
				channel_id,
				is_video_mode_ ? 2 : 1,
				(int)why,
				std::vector<std::string>{session_id, nim::Client::GetCurrentUserAccount()},
				std::vector<int>{0, 0}
			);
		}
	}

	void AvChatComponent::handleOtherClientRejected(std::shared_ptr<nim::SignalingNotifyInfo> notifyInfo)
	{
		//主叫方的通话邀请在其他端被拒接，通知上层关闭界面、不关channel
		compEventHandler_.lock()->onOtherClientReject();
		status_ = idle;
	}

	void AvChatComponent::handleRejected(std::shared_ptr<nim::SignalingNotifyInfo> notifyInfo)
	{
		SignalingNotifyInfoReject* rejectedInfo = (SignalingNotifyInfoReject*)notifyInfo.get();
		QLOG_APP(L"handleRejected, from_account_id_: {0}") << rejectedInfo->from_account_id_;
		if (rejectedInfo->to_account_id_ != senderAccid)
			return;

		status_ = idle;
		//被叫方忙线会自动reject、塞入601作为标记，并发送忙线话单，主叫方无需发送话单，也无需调用hangup（hangup时会发送话单）
		if (rejectedInfo->custom_info_ == "601")
		{
			//上层处理onUserBusy事件时
			compEventHandler_.lock()->onUserBusy(rejectedInfo->from_account_id_);
			closeChannelInternal(rejectedInfo->channel_info_.channel_id_, nullptr);
		}
		else
			compEventHandler_.lock()->onUserReject(rejectedInfo->from_account_id_);
	}

	//频道中有成员加入，用户维护成员列表
	void AvChatComponent::handleJoin(std::shared_ptr<SignalingNotifyInfo> notifyInfo)
	{
		SignalingNotifyInfoJoin* joinInfo = (SignalingNotifyInfoJoin*)notifyInfo.get();
		QLOG_APP(L"handleJoin: accid: {0}, uid: {1}") << joinInfo->member_.account_id_ << joinInfo->member_.uid_;
		channelMembers_[joinInfo->member_.account_id_] = joinInfo->member_.uid_;

		//有自身以外的人加入频道
		if (joinInfo->member_.account_id_ != senderAccid)
		{
			//status_ = calling;
			QLOG_APP(L"handleJoin, onUserEnter, userID: {0}") << joinInfo->member_.account_id_;
			compEventHandler_.lock()->onUserEnter(joinInfo->member_.account_id_, joinInfo->member_.uid_);
		}
	}

	void AvChatComponent::handleLeave(std::shared_ptr<nim::SignalingNotifyInfo> notifyInfo)
	{
		SignalingNotifyInfoLeave* leaveInfo = (SignalingNotifyInfoLeave*)notifyInfo.get(); 
		QLOG_APP(L"handleLeave, from_account_id_: {0}, senderAccid: {1}") << leaveInfo->from_account_id_ << senderAccid;

		compEventHandler_.lock()->onUserLeave(leaveInfo->from_account_id_, channelMembers_[leaveInfo->from_account_id_]);
	}
	void AvChatComponent::handleClose(std::shared_ptr<nim::SignalingNotifyInfo> notifyInfo)
	{
		SignalingNotifyInfoClose* closeInfo = (SignalingNotifyInfoClose*)notifyInfo.get();
		QLOG_APP(L"handleClose, from_account_id_: {0}, senderAccid: {1}") << closeInfo->from_account_id_ << senderAccid;
		
		status_ = idle;
		compEventHandler_.lock()->onCallEnd();
		
	}
	void AvChatComponent::handleCancelInvite(std::shared_ptr<nim::SignalingNotifyInfo> notifyInfo)
	{
		SignalingNotifyInfoCancelInvite* cancelInfo = (SignalingNotifyInfoCancelInvite*)notifyInfo.get();
		
		compEventHandler_.lock()->onUserCancel(cancelInfo->from_account_id_);
		status_ = idle;
	}
	void AvChatComponent::signalingNotifyCb(std::shared_ptr<SignalingNotifyInfo> notifyInfo)
	{
		switch (notifyInfo->event_type_)
		{
		case nim::kNIMSignalingEventTypeInvite:
			handleInvited(notifyInfo);
			break;
		case nim::kNIMSignalingEventTypeAccept:
			handleAccepted(notifyInfo);
			break;
		case nim::kNIMSignalingEventTypeJoin:
			handleJoin(notifyInfo);
			break;
		case nim::kNIMSignalingEventTypeReject:
			handleRejected(notifyInfo);
			break;
		case nim::kNIMSignalingEventTypeCancelInvite:
			handleCancelInvite(notifyInfo);
			break;
		case nim::kNIMSignalingEventTypeLeave:
			handleLeave(notifyInfo);
			break;
		case nim::kNIMSignalingEventTypeClose:
			handleClose(notifyInfo);
			break;
		case nim::kNIMSignalingEventTypeCtrl:
			handleControl(notifyInfo);
			break;
		default:
			break;
		}
	}

	void AvChatComponent::signalingMutilClientSyncCb(std::shared_ptr<nim::SignalingNotifyInfo> notifyInfo)
	{
		QLOG_APP(L"MutilClientSyncCb");
		switch (notifyInfo->event_type_)
		{
		case nim::kNIMSignalingEventTypeAccept:
			handleOtherClientAccepted(notifyInfo);
			break;
		case nim::kNIMSignalingEventTypeReject:
			handleOtherClientRejected(notifyInfo);
			break;
		default:
			QLOG_APP(L"MutilClientSyncCb event_type: {0}")<< notifyInfo->event_type_;
			break;
		}
	}
	void AvChatComponent::signalingOfflineNotifyCb(std::list<std::shared_ptr<nim::SignalingNotifyInfo>> notifyInfo)
	{
		std::list<std::shared_ptr<SignalingNotifyInfo>> notifyInfoList = notifyInfo;
		std::shared_ptr <nim::SignalingNotifyInfo> inviteInfo = nullptr;
		std::set<std::string> cancelSet;
		for (std::shared_ptr<SignalingNotifyInfo> info : notifyInfoList) {
			if (info != nullptr) {
				if (info->event_type_ == kNIMSignalingEventTypeInvite) {
					if (!inviteInfo || info->timestamp_ > inviteInfo->timestamp_) {
						inviteInfo = info;
					}
				}
				else if (info->event_type_ == kNIMSignalingEventTypeCancelInvite) {
					nim::SignalingNotifyInfoCancelInvite* cancelInfo = (nim::SignalingNotifyInfoCancelInvite*)(&(*info));
					cancelSet.insert(cancelInfo->request_id_);
				}
			}
		
		}
	
		nim::SignalingNotifyInfoInvite* lastInviteInfo = (nim::SignalingNotifyInfoInvite*)(&(*inviteInfo));
		if (lastInviteInfo != nullptr) {
			int nCount = cancelSet.count(lastInviteInfo->request_id_);
			//BOOL isValid = (inviteInfo != nullptr) && !inviteInfo->channel_info_.invalid_ && (cancelSet.find(lastInviteInfo->request_id_) == cancelSet.end());
			BOOL isValid = (inviteInfo != nullptr) && (cancelSet.find(lastInviteInfo->request_id_) == cancelSet.end());
			if (isValid) {
				handleInvited(inviteInfo);
			}
		}
	}
	///////////////////////////////G2事件///////////////////////////////
	void AvChatComponent::onJoinChannel(nertc::channel_id_t cid, nertc::uid_t uid, nertc::NERtcErrorCode result, uint64_t elapsed) {
		QLOG_APP(L"onJoinChannel");
		//rtcEngine_->enableLocalAudio(true);
	}
	void AvChatComponent::onUserJoined(nertc::uid_t uid, const char* user_name)
	{
		QLOG_APP(L"onUserJoined");
		if (avchat_type == kAvChatMulti) compEventHandler_.lock()->onUserEnter("", uid);
		VideoManagerG2::GetInstance()->SetRemoteCanvas(uid);
	}
	void AvChatComponent::onUserLeft(nertc::uid_t uid, nertc::NERtcSessionLeaveReason reason)
	{
		if (reason != 4) compEventHandler_.lock()->onUserLeave(getAccid(uid), uid);
		else compEventHandler_.lock()->onUserDisconnect(getAccid(uid), uid);
	}
	void AvChatComponent::onUserAudioStart(nertc::uid_t uid)
	{
		compEventHandler_.lock()->onAudioAvailable(getAccid(uid), uid, true);
	}
	void AvChatComponent::onUserAudioStop(nertc::uid_t uid)
	{
		compEventHandler_.lock()->onAudioAvailable(getAccid(uid), uid, false);
	}

	void AvChatComponent::onUserVideoStart(nertc::uid_t uid, nertc::NERtcVideoProfileType max_profile)
	{
		int ret = rtcEngine_->subscribeRemoteVideoStream(uid, nertc::kNERtcRemoteVideoStreamTypeHigh, true);
		compEventHandler_.lock()->onCameraAvailable(getAccid(uid), uid, true);
	}
	void AvChatComponent::onUserVideoStop(nertc::uid_t uid)
	{
		compEventHandler_.lock()->onCameraAvailable(getAccid(uid), uid, false);
	}
	void AvChatComponent::onDisconnect(nertc::NERtcErrorCode reason) {
		compEventHandler_.lock()->onDisconnect(reason);
	}

	void AvChatComponent::onNetworkQuality(const nertc::NERtcNetworkQualityInfo *infos, unsigned int user_count)
	{
		std::map<uint64_t, nertc::NERtcNetworkQualityType> network_quality;
		for (unsigned int i = 0; i < user_count; i++)
		{
			network_quality[infos[i].uid] = infos[i].tx_quality;
		}
		compEventHandler_.lock()->onUserNetworkQuality(network_quality);
	}
	///////////////////////////////////////////内部方法////////////////////////////////////////
	//Signaling::SignalingNotifyCallback 
	int64_t getUid(const std::list<SignalingMemberInfo>& list, const std::string& accid)
	{
		for (auto it : list)
		{
			if (it.account_id_ == accid)
				return it.uid_;
		}
		assert(false);
		return 0;
	}

	bool parseCustomInfo(const std::string& str, bool& isFromGroup, std::string& requestId, std::string& channelName)
	{
		requestId.clear();
		channelName.clear();

		Json::Value values;
		Json::Reader reader;
		if (!reader.parse(str, values) || !values.isObject())
		{
			QLOG_ERR(L"parse custom info failed: {0}") << str;
			return false;
		}
		
		isFromGroup = ((AVCHAT_MODULE_TYPE)values[kAvChatCallSingle].asInt() == kAvChatMulti);
		if (values.isMember(kAvChatCallRequestId))
		{
			requestId = values[kAvChatCallRequestId].asString();
		}

		if (values.isMember(kAvChatCallChannelName))
		{
			channelName = values[kAvChatCallChannelName].asString();
		}
		
		return true;
	}
}